Conversation
Adds Go bindings and matching Python modules for: action, array, cache, crypto, dns, entitlement, i18n, info, registry, scm, task. Also: AGENTS.md + RFC.md; register.go updated; Python __init__ re-exports; test_core.py expanded; runtime/interpreter_test.go extended. Pairs each new binding with a Python module at py/core/<name>.py so Python code can import core.<name> and call Go primitive impls through gpython. Pre-existing WIP captured for forward motion; bindings may evolve further as CorePy path B builds out (tasks.lthn.sh #13, children #72-#78).
Adds `core.medium.open("qdrant://host:port/collection")` returning a
QdrantMedium. Supports read/write of bytes + text via the Qdrant REST
API using stdlib http.client (mockable for tests).
Includes QdrantError class for HTTP/shape failures. 8 tests cover URL
parsing, REST request shape, bytes payloads, and error paths; all pass
via `uv run pytest py/tests/ -k medium`.
First piece of Path B (Mantis #13) — enables the OpenBrain MemoryProvider
plugin to reach Qdrant via core.medium instead of a bespoke client.
Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=72
…-trip GREEN Audit + fill against plans/code/core/py/RFC.md §5.1 primitive coverage map. NEW bindings (Go side + Python shim): - bindings/agent + py/core/agent.py - bindings/api + py/core/api.py - bindings/container + py/core/container.py - bindings/mcp + py/core/mcp.py - bindings/store + py/core/store.py - bindings/ws + py/core/ws.py Updated 19 existing bindings (action/array/cache/config/crypto/data/ entitlement/fs/i18n/log/math/medium/process/register/registry/scm/task/ typemap) for §5.2 binding-convention compliance. §9 First-Test Milestone: GREEN on bootstrap interpreter (echo round-trip verified via runtime/interpreter_test.go + new runtime/examples_test.go). Real gpython embedding (LetheanNetwork/gpython fork) remains separate upgrade scope; bootstrap stub keeps the binding contract honest until then. NEW tests: - runtime/examples_test.go — examples/echo.py round-trip via runtime - runtime/rfc_stub_modules_test.go — coverage assertion for §5.1 - py/tests/test_rfc_stub_modules.py — Python-side coverage parity §11 Status table values updated in integration report (RFC stays read-only; supervisor lands RFC update separately if needed).
…+ Tier 2 stub + corepy CLI
Sets up the gpy-0.1 → gpy-0.2 transition per RFC §11. Codebase is now
ready to swap in real LetheanNetwork/gpython without rewriting bindings.
Changes:
- runtime.Interpreter interface (runtime/interpreter.go) — bootstrap
moved to runtime/bootstrap/, gpython placeholder at runtime/gpython/
with `//go:build gpython` tag returning typed "backend not built"
error until the fork ships
- Backend selector via Options{Backend: "bootstrap"|"gpython"};
defaults to bootstrap
- All 27 binding packages updated to honor the new interface contract
- Tier 2 gopy stub at py/build/ — README + build.sh that detects
missing-gopy and exits 0 informationally, _build_stub.py that raises
typed NotImplementedError pointing operators at the proper Tier 2
build flow
- gpython readiness audit at docs/gpython-readiness.md — per-binding
classification (READY 54.8% / NEEDS-ADAPTATION 45.2% / BLOCKED 0%)
- cmd/corepy/main.go — standalone CLI (`corepy run <script>`,
`corepy run -e <code>`, `corepy modules`, `corepy version`)
- New tests: runtime/{bootstrap,gpython}/* + py/build/tests/* +
Python-side smoke parity for every Go-side binding test
Verification:
- GOWORK=off go build ./... + go test ./... all green
- python3 -m unittest discover passes both py/tests and py/build/tests
- /tmp/corepy run examples/echo.py prints "hello"
- /tmp/corepy version prints "corepy 0.2.0 backend=bootstrap"
AGENTS.md untouched.
Co-authored-by: Codex <noreply@openai.com>
… + binding catalog
Tier 2 CPython subprocess runner lands with timeout, stdout/stderr
capture, streaming, exit codes, and structured errors.
core.process.run_result(...) exposed in both Go bindings and Python
fallback surface. Build-tagged gpython backend boundary with bootstrap
smoke shim for -tags gpython. corepy CLI gains tier2 run|which
subcommands and -backend bootstrap|gpython selection.
- runtime/tier2/ — CPython subprocess runner (new package)
- runtime/gpython_{disabled,enabled}.go — build-tagged backend boundary
- runtime/gpython/interpreter.go — bootstrap smoke shim
- bindings/process/process.go + bindings/register/register.go — run_result
- py/core/process.py — Python fallback surface
- cmd/corepy/main.go + main_test.go — tier2 + backend CLI
- examples/primitive_pipeline.py — primitive composition demo
- go vet + go test (default + -tags gpython) all pass
- python -m unittest py/tests + py/build/tests all pass
Pass 3 sample against /Users/snider/Code/host-uk/core/plans/code/core/py/RFC.md
Pass 1: initial scaffold, Pass 2: 87f9a27 (interpreter abstraction + gpython audit + tier2 stub + corepy CLI)
Co-authored-by: Codex <noreply@openai.com>
…supported-import detection Tier 1 gpython gains stdlib shadow modules (json, os, os.path, subprocess, logging, hashlib, base64, socket) that route through CoreGO bindings. Shadows register separately from core.* namespace. Typed unsupported-import detection at the interpreter boundary, with a new -tier2-fallback CLI flag that auto-promotes to CPython subprocess when Tier 1 can't satisfy a builtin. - bindings/stdlib/stdlib.go (new) — 8 stdlib shadow modules - bindings/register/register.go — separate stdlib registration - runtime/interpreter.go + bootstrap/interpreter.go — typed unsupported-import - cmd/corepy/main.go — -tier2-fallback flag - examples/stdlib_shadow.py — stdlib shadow demo - 262 insertions; go vet + go test (default + -tags gpython) + python -m unittest all pass Pass 4 sample against /Users/snider/Code/host-uk/core/plans/code/core/py/RFC.md Pass 1: scaffold, Pass 2: 87f9a27, Pass 3: 8c7ced7 Co-authored-by: Codex <noreply@openai.com>
…pace + gpython smoke test Runtime gains Session / SessionCreator with persistent bootstrap namespace state — values defined in one call survive into the next. corepy gains a 'repl' subcommand with stateful line-by-line execution (CLI version 0.5.0). gpython-tagged stdlib-shadow smoke test added. - runtime/internal/contract/types.go — Session + SessionCreator - runtime/bootstrap/interpreter.go — persistent namespace state - runtime/gpython/interpreter.go — gpython session glue - cmd/corepy/main.go — repl subcommand - runtime/gpython_smoke_test.go (new) — stdlib-shadow smoke under -tags gpython - 162 insertions; default + -tags gpython + python -m unittest all pass Pass 5 sample against /Users/snider/Code/host-uk/core/plans/code/core/py/RFC.md Diff trajectory: R3 +1320 → R4 +830 → R5 +162 (steep decay — convergence trajectory) Co-authored-by: Codex <noreply@openai.com>
📝 WalkthroughWalkthroughIntroduces a complete two-tier Python runtime system called CorePy. Tier 1 provides a bootstrap interpreter executing Python-like scripts within a single Go binary, with ~30 CorePy modules (math, filesystem, crypto, DNS, process, caching, etc.) implemented as Go bindings. Tier 2 offers a managed CPython subprocess runner for numpy/torch/transformers. Includes comprehensive Python package surface, CLI tooling, extensive tests, and RFC documentation defining the architecture and parity strategy. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CLI
participant Tier1 as Tier 1<br/>Bootstrap
participant Tier2 as Tier 2<br/>CPython
User->>CLI: corepy run script.py
CLI->>Tier1: Execute via interpreter
alt Pure Python/Go operations
Tier1->>Tier1: Register CorePy modules
Tier1->>Tier1: Execute script<br/>(imports core.*)
Tier1-->>CLI: Output
else Unsupported import detected
Tier1-->>CLI: UnsupportedImportError
alt -tier2-fallback enabled
CLI->>Tier2: Delegate to CPython
Tier2->>Tier2: Execute with numpy/torch/etc.
Tier2-->>CLI: Result
else No fallback
CLI-->>User: Error
end
end
CLI-->>User: Final output
|
There was a problem hiding this comment.
Actionable comments posted: 3
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
🟠 Major comments (23)
py/core/info.py-82-94 (1)
82-94:⚠️ Potential issue | 🟠 MajorAvoid doing this work at import time.
_build_snapshot()shells out togo env GOVERSIONwhile the module is being imported, so a slow, broken, or hijackedgoonPATHcan stall or fail a plainimport core.info. Make_go_version()lazy or add a timeout and exception guard so import stays cheap and predictable.🛠️ Suggested fix
- completed = subprocess.run( - [go_binary, "env", "GOVERSION"], - capture_output=True, - check=False, - text=True, - ) + try: + completed = subprocess.run( + [go_binary, "env", "GOVERSION"], + capture_output=True, + check=False, + text=True, + timeout=1, + ) + except (OSError, subprocess.SubprocessError): + return os.environ.get("GOVERSION", "")Also applies to: 124-125
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/info.py` around lines 82 - 94, The module currently shells out to `go` at import via `_go_version()` (and similarly in `_build_snapshot`), which can block or fail; change `_go_version()` to be lazy and robust by (1) deferring execution until called (e.g., wrap with a cached lazy call or remove invocation at import), (2) invoking subprocess.run with a short timeout and wrapped in try/except to catch TimeoutError, CalledProcessError, and OSError, and (3) falling back to os.environ.get("GOVERSION", "") on any failure; apply the same timeout-and-exception-guard logic to the `go` invocation used by `_build_snapshot()` so imports remain cheap and non-blocking.py/core/json.py-24-32 (1)
24-32:⚠️ Potential issue | 🟠 MajorRemove manual UTF-8 decoding to preserve
json.loadsbyte encoding support.The current implementation decodes bytes as UTF-8 before passing to
jsonlib.loads(), which breaks support for JSON byte streams using UTF-16, UTF-32, or other encodings that the stdlib natively handles. Pass bytes directly tojsonlib.loads()instead.🛠️ Suggested fix
def loads(value: str | bytes) -> Any: """Deserialise JSON text or bytes. json.loads('{"name":"corepy"}') """ - if isinstance(value, bytes): - value = value.decode("utf-8") return jsonlib.loads(value)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/json.py` around lines 24 - 32, The loads function is currently decoding bytes to UTF-8 before calling jsonlib.loads, which prevents the stdlib JSON decoder from handling other byte encodings; instead remove the manual decoding in loads and pass the original value (whether str or bytes) directly to jsonlib.loads so that jsonlib.loads(value) can detect and handle byte encodings correctly (update the function body in loads to stop calling .decode and simply return jsonlib.loads(value)).RFC.md-403-408 (1)
403-408:⚠️ Potential issue | 🟠 MajorFix the
uv pip installsubdirectory.The source tree on Lines 371-378 shows
pyproject.tomlunderpy/, so#subdirectory=py/corepoints past the packaging root. As written, this install command is very likely to fail for anyone following the RFC.🛠️ Minimal doc fix
-uv pip install "core @ git+ssh://git@forge.lthn.ai:2223/core#subdirectory=py/core" +uv pip install "core @ git+ssh://git@forge.lthn.ai:2223/core#subdirectory=py"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@RFC.md` around lines 403 - 408, The example install command uses the wrong pip URL subdirectory; update the `uv pip install` example so the `#subdirectory` points to the packaging root containing pyproject.toml (change `#subdirectory=py/core` to `#subdirectory=py` in the example command string `core @ git+ssh://git@forge.lthn.ai:2223/core#subdirectory=...`) so pip can find the package metadata and install correctly.py/core/cache.py-135-149 (1)
135-149:⚠️ Potential issue | 🟠 MajorTreat malformed cache payloads as absent before
get()indexesentry["data"].Right now any JSON object with a numeric
expires_atpasses validation, even ifdatais missing. In that caseget()raisesKeyErrorinstead of falling back to the provided default.🛠️ Minimal guard
- if not isinstance(entry, dict): + if not isinstance(entry, dict) or "data" not in entry: return None🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/cache.py` around lines 135 - 149, The _load_entry function currently returns entries that lack a "data" key, causing get() to raise KeyError; update _load_entry (the method named _load_entry and the code path that get() uses to access entry["data"]) to validate that "data" is present (and optionally of an expected type) before returning the entry, and if "data" is missing or malformed treat the cache as absent by unlinking the file (target.unlink(missing_ok=True)) and returning None so get() falls back to the provided default.py/core/cache.py-115-133 (1)
115-133:⚠️ Potential issue | 🟠 Major
keys()is exposing expired entries as if they still exist.This path walk never applies the expiry rules from
_load_entry(), so stale files remain visible inkeys()until some other call happens to touch them. That makeskeys()disagree withget()andhas()about what is actually present.🛠️ One straightforward fix
for target in sorted(self._base_dir.rglob("*.json")): if not target.is_file(): continue key = target.relative_to(self._base_dir).as_posix()[:-5] if normalized_prefix and key != normalized_prefix and not key.startswith(f"{normalized_prefix}/"): continue + if self._load_entry(key) is None: + continue keys.append(key) return keys🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/cache.py` around lines 115 - 133, The keys() implementation currently lists all JSON files without applying expiry logic, so update keys() to consult the same expiry logic used by _load_entry()/get()/has(): for each candidate file (the loop over self._base_dir.rglob("*.json")), call _load_entry(path) or a shared helper that checks/loads metadata and returns None for expired entries; if the call indicates the entry is expired, delete the file (or skip it) and do not append the key, otherwise append the key as before—this ensures keys(), get(), and has() are consistent in treating expired entries.py/core/config.py-145-151 (1)
145-151:⚠️ Potential issue | 🟠 MajorThe functional
getwrapper drops thedefaultargument.
Config.get()supports a fallback value, butconfig.get(...)does not expose it, so the functional surface cannot expressconfig.get(cfg, "missing", 42). That breaks parity between the wrapper API and the underlying handle.🛠️ Minimal fix
-def get(config_value: Config, key: str) -> Any: +def get(config_value: Config, key: str, default: Any = None) -> Any: @@ - return config_value.get(key) + return config_value.get(key, default)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/config.py` around lines 145 - 151, The functional wrapper get currently drops the fallback argument supported by Config.get; update the signature of get(config_value: Config, key: str, default: Any = None) -> Any and forward that default to the underlying call (i.e., call config_value.get(key, default)); also update the docstring/example to show using the default parameter. This ensures parity between the wrapper get and Config.get.bindings/typemap/typemap.go-195-205 (1)
195-205:⚠️ Potential issue | 🟠 MajorTreat omitted optional errors as
nil.
OptionalErrorstill returnsexpected argument %dwhen the slot is missing, so bindings that advertise an optional error/value cannot support zero-argument calls. Returning(nil, nil)forindex >= len(arguments)keeps the helper aligned with that contract. Based on learnings: Preserve parity between the Go bindings/runtime contract and the Python package surface when changing primitive names, signatures, or import paths.Suggested fix
func OptionalError(arguments []any, index int, functionName string) (error, error) { - if index >= len(arguments) { - return nil, fmt.Errorf("%s expected argument %d", functionName, index) - } - if arguments[index] == nil { + if index >= len(arguments) || arguments[index] == nil { return nil, nil } return ExpectError(arguments, index, functionName) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/typemap/typemap.go` around lines 195 - 205, OptionalError currently returns an error when the argument slot is missing; change it to treat omitted optional errors as nil by returning (nil, nil) when index >= len(arguments) so callers that expect optional error semantics can call with fewer args. In the OptionalError function, replace the branch that returns fmt.Errorf("%s expected argument %d", functionName, index) with a simple return nil, nil, and keep the existing nil-check and fallback to ExpectError(arguments, index, functionName) for present non-nil values.bindings/err/err.go-55-80 (1)
55-80:⚠️ Potential issue | 🟠 Major
wrapcannot actually omit the cause argument.
core.err.wrap("op", "msg")will fail on Line 56 because the operation string is parsed as anerror. If the cause is meant to be optional, this needs to detect whether argument 0 is an error/nil and shift the string indices accordingly. Based on learnings: Preserve parity between the Go bindings/runtime contract and the Python package surface when changing primitive names, signatures, or import paths.Suggested fix
func wrap(arguments ...any) (any, error) { - sourceError, err := typemap.OptionalError(arguments, 0, "core.err.wrap") - if err != nil { - return nil, err - } - operation, err := typemap.ExpectString(arguments, 1, "core.err.wrap") + var ( + sourceError error + err error + offset int + ) + if len(arguments) > 0 { + if arguments[0] == nil { + offset = 1 + } else if value, ok := arguments[0].(error); ok { + sourceError = value + offset = 1 + } + } + operation, err := typemap.ExpectString(arguments, offset, "core.err.wrap") if err != nil { return nil, err } - message, err := typemap.ExpectString(arguments, 2, "core.err.wrap") + message, err := typemap.ExpectString(arguments, offset+1, "core.err.wrap") if err != nil { return nil, err } code := "" - if len(arguments) > 3 { - code, err = typemap.ExpectString(arguments, 3, "core.err.wrap") + if len(arguments) > offset+2 { + code, err = typemap.ExpectString(arguments, offset+2, "core.err.wrap") if err != nil { return nil, err } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/err/err.go` around lines 55 - 80, The wrap function incorrectly assumes argument 0 is the optional cause; update wrap to detect whether the first argument is an error/nil and shift parsing of operation/message/code accordingly: use typemap.OptionalError on args[0] and if it returns a non-nil error value treat it as the cause, otherwise treat args[0] as the operation string and call typemap.ExpectString on the shifted indices for operation (arg0 or arg1), message, and optional code; finally call core.Wrap or core.WrapCode with the resolved sourceError, operation, message, and code. Ensure you keep using typemap.OptionalError, typemap.ExpectString, and return core.Wrap/core.WrapCode as before.py/build/build.sh-8-17 (1)
8-17:⚠️ Potential issue | 🟠 MajorPrerequisite failures should return a non-zero exit code.
The script currently reports success when
gopyor the selected Python binary is missing. That can silently skip Tier 2 builds in CI/CD and local scripts.Suggested fix
if ! command -v gopy >/dev/null 2>&1; then - echo "Tier 2 build requires gopy + CPython 3.13+ -- see README.md" - exit 0 + echo "Tier 2 build requires gopy + CPython 3.13+ -- see README.md" >&2 + exit 1 fi python_bin="${PYTHON:-python3.13}" if ! command -v "${python_bin}" >/dev/null 2>&1; then - echo "Tier 2 build requires gopy + CPython 3.13+ -- see README.md" - exit 0 + echo "Tier 2 build requires gopy + CPython 3.13+ -- see README.md" >&2 + exit 1 fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/build/build.sh` around lines 8 - 17, The current prereq checks in build.sh use exit 0 which signals success; change those to non-zero exits so failures propagate: update both checks that test for gopy (command -v gopy) and the python binary referenced by python_bin (python_bin="${PYTHON:-python3.13}" and command -v "${python_bin}") to call exit 1 (or another non-zero code) instead of exit 0 so CI/local callers detect the missing prerequisites.bindings/crypto/crypto.go-101-113 (1)
101-113:⚠️ Potential issue | 🟠 Major
random_bytesneeds an upper bound on requested size.A caller-controlled
sizeis used for direct allocation. Very large values can cause OOM conditions and take down the runtime process.Suggested fix
+const maxRandomBytes = 1 << 20 // 1 MiB; tune to the runtime contract. + func randomBytes(arguments ...any) (any, error) { size, err := typemap.ExpectInt(arguments, 0, "core.crypto.random_bytes") if err != nil { return nil, err } if size < 0 { return nil, fmt.Errorf("core.crypto.random_bytes expected a non-negative size") } + if size > maxRandomBytes { + return nil, fmt.Errorf("core.crypto.random_bytes size exceeds maximum of %d bytes", maxRandomBytes) + } buffer := make([]byte, size) if _, err := cryptorand.Read(buffer); err != nil { return nil, fmt.Errorf("core.crypto.random_bytes failed to read randomness: %w", err) } return buffer, nil }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/crypto/crypto.go` around lines 101 - 113, The randomBytes function currently allocates a caller-controlled number of bytes which can cause OOM; add an explicit upper bound constant (e.g. MaxRandomBytes) and validate size <= MaxRandomBytes after the existing non-negative check, returning a clear error if exceeded; only proceed to make([]byte, size) and call cryptorand.Read(buffer) when size is within the allowed limit so huge allocations are prevented (update references in randomBytes to use the constant and the new validation).bindings/service/service.go-81-100 (1)
81-100:⚠️ Potential issue | 🟠 MajorDo not use
context.Background()for lifecycle calls.These operations can block indefinitely and cannot be cancelled or timed out, so a hung service will stall the interpreter. Thread through a request-scoped context from the runtime, or apply a bounded timeout here if no caller context exists.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/service/service.go` around lines 81 - 100, startAll and stopAll currently call instance.ServiceStartup(context.Background(), ...) and instance.ServiceShutdown(context.Background()) which uses an uncancelable global context; instead accept or derive a request-scoped context and/or apply a bounded timeout. Modify startAll and stopAll to obtain a context from the runtime/request (e.g., read a context argument or use an existing typemap helper) and pass that to ServiceStartup and ServiceShutdown, or if no caller context is available create a cancellable context with a sensible timeout via context.WithTimeout and defer cancel() before calling the methods; ensure you use the same context variable for both ServiceStartup and ServiceShutdown calls in startAll/stopAll respectively and include cancellation handling.bindings/i18n/i18n.go-116-134 (1)
116-134:⚠️ Potential issue | 🟠 MajorOnly persist the locale after the translator accepts it.
setLanguagewritesitem.localebefore validating the new language with the translator. If the translator rejects the language, the handle now reports a locale that was never accepted.🔧 Suggested fix
- item.locale = lang - if item.translator != nil { - if err := item.translator.SetLanguage(lang); err != nil { - return nil, err - } - } + if item.translator != nil { + if err := item.translator.SetLanguage(lang); err != nil { + return nil, err + } + } + item.locale = lang🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/i18n/i18n.go` around lines 116 - 134, The function setLanguage currently assigns item.locale before validating with item.translator, so move the assignment until after translator validation: call item.translator.SetLanguage(lang) first (only when item.translator != nil), returning the error if it fails, and then set item.locale = lang; if item.translator is nil simply set item.locale = lang; ensure item is returned unchanged on error. This change targets the setLanguage function and the calls to item.translator.SetLanguage(lang).bindings/i18n/i18n.go-68-87 (1)
68-87:⚠️ Potential issue | 🟠 MajorMake translator swaps atomic.
setTranslatorstores the new translator beforeSetLanguagesucceeds. If that call fails, the handle is left half-updated and subsequent calls will see the new translator even though the operation returned an error.🔧 Suggested fix
- item.translator = translatorValue - if item.locale != "" { - if err := item.translator.SetLanguage(item.locale); err != nil { - return nil, err - } - } + if item.locale != "" { + if err := translatorValue.SetLanguage(item.locale); err != nil { + return nil, err + } + } + item.translator = translatorValue🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/i18n/i18n.go` around lines 68 - 87, The setTranslator function currently assigns item.translator before calling translator.SetLanguage, leaving the handle partially updated if SetLanguage fails; change the logic in setTranslator (and use the translator type/value) to first validate and call SetLanguage on the new translator when item.locale != "" and only assign item.translator = translatorValue after SetLanguage succeeds (or alternatively perform the assignment and revert on error), ensuring the swap is atomic and any error leaves item.translator unchanged; locate references to setTranslator, item.translator, translator.SetLanguage, item.locale and expectHandle to implement this change.runtime/gpython_smoke_test.go-27-45 (1)
27-45:⚠️ Potential issue | 🟠 MajorQuote the
gobinary path before embedding it.Interpolating
goBinarydirectly into the Python snippet makes this smoke test brittle on paths with backslashes or quotes, so it can fail on otherwise valid environments.🔧 Suggested fix
- print(subprocess.check_output(["` + goBinary + `", "env", "GOOS"])) + print(subprocess.check_output([` + strconv.Quote(goBinary) + `, "env", "GOOS"]))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@runtime/gpython_smoke_test.go` around lines 27 - 45, The embedded goBinary path is injected raw into the Python snippet (see variable goBinary and the interpreter.Run call), which breaks on paths with quotes or backslashes; fix by properly quoting/escaping the path before embedding—e.g. replace the inline concat with a quoted form using strconv.Quote or fmt.Sprintf("%q", goBinary) when constructing the Python string so the path is safely wrapped/escaped inside the triple-quoted snippet passed to interpreter.Run.bindings/config/config.go-92-131 (1)
92-131:⚠️ Potential issue | 🟠 MajorDo not silently coerce invalid env fallbacks.
intValueandboolValuecurrently swallowstrconv.Atoi/strconv.ParseBoolfailures and return0/false. That makes a misconfigured environment indistinguishable from an unset key and can hide configuration mistakes.♻️ Suggested fix
func intValue(arguments ...any) (any, error) { @@ if value, ok := lookupEnvironment(key); ok { parsed, err := strconv.Atoi(value) - if err == nil { - return parsed, nil + if err != nil { + return nil, err } + return parsed, nil } return 0, nil } @@ if value, ok := lookupEnvironment(key); ok { parsed, err := strconv.ParseBool(value) - if err == nil { - return parsed, nil + if err != nil { + return nil, err } + return parsed, nil } return false, nil }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/config/config.go` around lines 92 - 131, The intValue and boolValue helpers currently ignore strconv.Atoi/ParseBool errors and return zero values, hiding invalid environment fallbacks; update intValue and boolValue so that when lookupEnvironment(key) returns a string but parsing fails you return the parsing error (wrapped or returned directly) instead of silently returning 0/false, preserving existing behavior where config.Get(key).OK short-circuits to the config value and keeping typemap.Expect* errors unchanged; reference the intValue and boolValue functions and ensure the returned error includes the original strconv error message for easier debugging.py/core/crypto.py-16-22 (1)
16-22:⚠️ Potential issue | 🟠 MajorSHA-1 is intentionally exported in the public API; consider whether this should be isolated or removed.
sha1is explicitly listed incrypto.__all__and therefore part of the public surface. Since SHA-1 is cryptographically broken, exposing it without explicit guidance risks misuse. The function is only tested (py/tests/test_core.py:222) and has no production consumers. If SHA-1 is not required for backward compatibility or documented interoperability, either remove it or relocate it to alegacyor internal namespace with explicit "non-security" documentation to discourage accidental misuse.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/crypto.py` around lines 16 - 22, The sha1 function is exposed in the public API via crypto.__all__, which risks accidental use of broken SHA-1; either remove it from the public surface or move it to a legacy/internal namespace and update references/tests accordingly. Specifically, either (A) remove "sha1" from crypto.__all__ and adjust py/tests/test_core.py to import/use the internal test helper or update the test to use a non-crypto utility, or (B) relocate the function sha1 into a new internal module (e.g., crypto_legacy or _crypto_internal), add clear docstring noting it is non-secure/legacy, export only for tests, and update imports in py/tests/test_core.py to reference the new module; ensure no other production modules import crypto.sha1.bindings/cache/cache.go-314-316 (1)
314-316:⚠️ Potential issue | 🟠 MajorDo not swallow JSON decode failures in
get.Line 315 currently treats malformed cache content as “not found”. This masks data corruption and silently changes behaviour.
Proposed fix
if err := decoder.Decode(&decoded); err != nil { - return nil, false, nil + return nil, false, fmt.Errorf("core.cache.get failed to decode entry %q: %w", path, err) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/cache/cache.go` around lines 314 - 316, The get function currently swallows JSON decoding errors (the decoder.Decode(&decoded) failure) and returns (nil, false, nil) which hides corruption; change the error path to propagate the decoding error instead of treating it as “not found”: when decoder.Decode(&decoded) returns an error, return nil, false, err (or wrap the error with context) so callers can detect and handle malformed cache entries; update any callers/tests that expect the old silent behavior if necessary.py/core/strings.py-66-85 (1)
66-85:⚠️ Potential issue | 🟠 Major
splitandsplit_nrequire test coverage and explicit handling for empty separators.Lines 72 and 85 delegate directly to Python's
str.split(), which raisesValueErroron empty separators. There is no test coverage for the empty separator case (test_core.py only covers non-empty:"key=value=extra", "=", 2). If the Go-sidecore.Splitimplementation handles empty separators, the Python surface will diverge. Add:
- Test cases covering empty separator inputs
- Explicit error handling or documented behaviour for
separator == ""This ensures parity with the Go bindings and prevents surprises at the API boundary.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/strings.py` around lines 66 - 85, split and split_n currently delegate to str.split and will raise a ValueError on an empty separator without tests; update both functions (split and split_n) to explicitly handle separator == "" by mirroring the Go/core.Split behavior (inspect core.Split and either raise a clear ValueError with a descriptive message or implement the same empty-separator semantics), add unit tests in test_core.py covering empty-separator inputs for both split and split_n (including limit edge-cases like 0 and negative), and document the chosen behavior in the function docstrings.runtime/interpreter_test.go-63-78 (1)
63-78:⚠️ Potential issue | 🟠 MajorEnsure interpreters are always closed in the shared test helper.
newTestInterpreterreturns live interpreters withoutt.Cleanup, so resources remain open across many tests.💡 Suggested fix
func newTestInterpreter(t *testing.T) testInterpreter { t.Helper() @@ caller, ok := interpreter.(testInterpreter) if !ok { + _ = interpreter.Close() t.Fatalf("interpreter does not expose direct calls: %T", interpreter) } + t.Cleanup(func() { + _ = caller.Close() + }) return caller }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@runtime/interpreter_test.go` around lines 63 - 78, newTestInterpreter returns a live interpreter and doesn't register a test cleanup, leaking resources; update newTestInterpreter to call t.Cleanup to close the interpreter when the test finishes (invoke the interpreter's shutdown method — e.g. interpreter.Close() or the appropriate teardown on the testInterpreter interface) after successful creation and module registration so the interpreter returned by newTestInterpreter is always closed automatically; reference newTestInterpreter, testInterpreter, corepyruntime.New and register.DefaultModules when making the change.bindings/stdlib/stdlib.go-294-315 (1)
294-315:⚠️ Potential issue | 🟠 MajorCapture
stderrexplicitly inrunCommandto reliably return stderr output.The current implementation uses
cmd.Output()without settingcmd.Stderr, which means the returnedstderrfield will always be empty even when the command writes to stderr. Theexec.ExitError.Stderrfield is only populated ifcmd.Stderris explicitly configured before execution.Suggested fix
import ( + "bytes" "crypto/sha1" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "net" "os" "os/exec" "path/filepath" "slices" "strings" func runCommand(functionName string, arguments []any, check bool) (map[string]any, error) { commandLine, err := commandLineArguments(arguments, functionName) if err != nil { return nil, err } cmd := exec.Command(commandLine[0], commandLine[1:]...) - output, err := cmd.Output() - stderr := "" + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() exitCode := 0 if err != nil { exitCode = -1 if exitErr, ok := err.(*exec.ExitError); ok { exitCode = exitErr.ExitCode() } if check { + stderrText := stderr.String() - if stderr != "" { - return nil, fmt.Errorf("%s exited with status %d: %s", functionName, exitCode, firstLine(stderr)) + if stderrText != "" { + return nil, fmt.Errorf("%s exited with status %d: %s", functionName, exitCode, firstLine(stderrText)) } return nil, fmt.Errorf("%s exited with status %d: %w", functionName, exitCode, err) } } return map[string]any{ "args": commandLine, - "stdout": string(output), - "stderr": stderr, + "stdout": stdout.String(), + "stderr": stderr.String(), "returncode": exitCode, "timed_out": false, "ok": exitCode == 0, "core_shadow": true, }, nil }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bindings/stdlib/stdlib.go` around lines 294 - 315, The runCommand implementation uses cmd.Output() but never wires cmd.Stderr, so stderr is always empty; fix runCommand by creating a bytes.Buffer (e.g., stderrBuf) and setting cmd.Stderr = &stderrBuf before calling cmd.Output(), then use stderrBuf.String() as the stderr return value (and, when err is an *exec.ExitError, prefer exitErr.Stderr if populated but fall back to stderrBuf.String()); ensure you still set exitCode from exitErr.ExitCode() and return the captured stderr in the returned map (referencing cmd, cmd.Output, cmd.Stderr, stderrBuf, and exitErr).py/core/task.py-138-150 (1)
138-150:⚠️ Potential issue | 🟠 MajorFix A002 builtin shadowing in
new_stepwhile preserving backwards compatibility withinput=callers.The
inputparameter is flagged as A002 (shadows Python builtin) and will fail ruff linting, which is enabled in the repository configuration. The parameter is actively used by the Go runtime bindings (task.new_step("consume", input="previous")), so renaming without compatibility handling would break the API contract. Acceptinputvia**kwargswith an internal non-shadowing name to preserve the public API whilst passing linting.Suggested fix (maintains backwards compatibility)
def new_step( action: str, with_values: dict[str, Any] | None = None, async_step: bool = False, - input: str = "", + input_value: str = "", + **kwargs: Any, ) -> Step: """Create a Step value. task.new_step("echo", {"text": "hello"}) """ - return Step(action=action, with_values={} if with_values is None else dict(with_values), async_step=async_step, input=input) + if "input" in kwargs: + input_value = str(kwargs.pop("input")) + if kwargs: + unexpected = ", ".join(sorted(kwargs)) + raise TypeError(f"task.new_step got unexpected keyword argument(s): {unexpected}") + return Step( + action=action, + with_values={} if with_values is None else dict(with_values), + async_step=async_step, + input=input_value, + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/task.py` around lines 138 - 150, Rename the internal parameter to avoid shadowing by removing the explicit input parameter from new_step's signature and accept **kwargs instead; inside new_step, extract the public "input" kwarg (e.g., input_value = kwargs.pop("input", "")) and use that internal name when constructing the Step, preserving the default "" and supporting callers that pass input="..."; also validate that no unexpected kwargs remain (raise TypeError if kwargs not empty) so other callers get a clear error, and keep with_values handling and async_step unchanged—this preserves the public input= API used by Go bindings while fixing the A002 builtin shadowing lint.py/core/data.py-117-123 (1)
117-123:⚠️ Potential issue | 🟠 MajorBlock path traversal when resolving mounted paths.
At Line 117-123,
_resolvepermits..segments (for example,fixtures/../secret.txt) to escape the mounted root. This impactsread_file,read_string,list, andextract.Proposed fix
def _resolve(self, logical_path: str) -> Path: mount_name, _, relative_path = logical_path.partition("/") if mount_name not in self._mounts: raise KeyError(f"mount not found: {mount_name}") - root = self._mounts[mount_name] - return root if relative_path == "" else root / relative_path + root = self._mounts[mount_name].resolve() + candidate = root if relative_path == "" else (root / relative_path).resolve() + if candidate != root and root not in candidate.parents: + raise ValueError(f"path escapes mount root: {logical_path}") + return candidate🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/data.py` around lines 117 - 123, The _resolve method currently allows path traversal via '..'; fix by normalizing and resolving the combined path and verifying it stays under the mount root: after obtaining root from self._mounts and computing relative_path, compute root_resolved = root.resolve() and candidate = (root / relative_path).resolve() (use strict=False if needed), then if candidate is not inside root_resolved (use Path.is_relative_to(root_resolved) or compare root_resolved in candidate.parents) raise a KeyError/ValueError indicating forbidden path traversal; return root_resolved for empty relative_path or candidate otherwise. Ensure this change is applied in the _resolve function so read_file/read_string/list/extract use the safe path.py/core/medium.py-186-195 (1)
186-195:⚠️ Potential issue | 🟠 MajorDo not clobber the text field for non-UTF-8 bytes.
For binary payloads, writing
""intoself.payload_fieldmakesread_text()return an empty string instead of exposing that the value is not UTF-8. Please omit the text field when decoding fails so the bytes path remains authoritative.🔧 Proposed fix
def write_bytes(self, value: bytes) -> bytes: """Write bytes into a Qdrant point payload.""" payload = {self.bytes_field: base64.b64encode(value).decode("ascii")} try: payload[self.payload_field] = value.decode("utf-8") except UnicodeDecodeError: - payload[self.payload_field] = "" + pass self._set_payload(payload) return value🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/medium.py` around lines 186 - 195, In write_bytes, avoid clobbering the text field when binary data isn't UTF-8: in the write_bytes method (referencing self.bytes_field, self.payload_field and _set_payload), build payload with the base64-encoded bytes under self.bytes_field and only set self.payload_field to the decoded string when value.decode("utf-8") succeeds; if a UnicodeDecodeError occurs, do not add self.payload_field to payload (omit it) so the bytes path remains authoritative before calling self._set_payload(payload) and returning value.
🧹 Nitpick comments (5)
py/core/dns.py (1)
50-59: Tighten_uniqueto an iterable type.The implementation iterates over
values, but the signature allows plainobject, which is not iterable from a type-checker’s point of view. Please annotate this as anIterable[...]so the helper stays consistent with the typed Python surface. As per coding guidelines:py/core/**/*.py: Keep the Python package typed and consistent with the pyproject.toml target of Python 3.12+.🛠️ Suggested typing fix
+from collections.abc import Iterable import socket @@ -def _unique(values: list[str] | tuple[str, ...] | object) -> list[str]: +def _unique(values: Iterable[object]) -> list[str]:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/dns.py` around lines 50 - 59, The _unique function's parameter is currently typed as list[str] | tuple[str, ...] | object which allows non-iterable types; change the annotation to accept an iterable (e.g., values: Iterable[str] | Iterable[object] or simply Iterable[object] depending on desired input), import Iterable from typing, and keep the function body unchanged so static type-checkers know values is iterable; update the signature for the _unique function accordingly and ensure any callers still match the new Iterable typing.py/core/__init__.py (2)
15-21: Broadenecho()so it preserves the caller's type.This is an identity helper, so typing it as
strnarrows the public API unnecessarily and makes valid non-string uses fail static checking.Suggested change
+from typing import TypeVar + +T = TypeVar("T") + -def echo(value: str) -> str: +def echo(value: T) -> T:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/__init__.py` around lines 15 - 21, The echo function is currently typed to str which unnecessarily narrows its API; change it to be generic using a TypeVar (e.g., T = TypeVar("T")) and annotate the function as def echo(value: T) -> T so it preserves the caller's type. Add the TypeVar import from typing (or typing_extensions if used elsewhere) and update the echo signature and docstring example if needed to reflect generic behavior; keep the implementation returning value unchanged and ensure no other behavior changes.
24-56: Sort__all__to keep the export list lint-clean.Ruff already flags this, and sorting the export list will keep the module surface deterministic.
Suggested change
__all__ = [ - "array", "action", "agent", "api", + "array", "cache",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/__init__.py` around lines 24 - 56, The exported names in the __all__ list are unsorted and trigger linter warnings; sort the entries in the __all__ list (the "__all__" symbol) alphabetically (case-insensitive) while preserving the existing string quotes, commas, and list structure so the module export order is deterministic and Ruff warnings are resolved.py/core/task.py (1)
236-240: Add exception observability to async step execution.Catching
Exceptionand silently returning in the daemon thread prevents any visibility into async step failures. This makes debugging difficult and hides real problems from operators and test runners. Consider logging the exception or using an exception handler callback to surface failures.def _run_async(current_action: action_module.Action, step_values: dict[str, Any], context: Any) -> None: try: current_action.run(step_values, context) except Exception: return🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@py/core/task.py` around lines 236 - 240, The _run_async function currently swallows all exceptions from current_action.run, losing observability; change the except block to "except Exception as e:" and surface the error by logging the exception (e.g., use logging.getLogger(__name__).exception(...) or an existing module logger) including identifying info like current_action (or current_action.name) and step_values, and if the provided context exposes an error callback (e.g., context.on_error or similar), invoke it with the exception and current_action so callers/tests can react; ensure you import logging if needed and avoid re-raising so the daemon thread stays alive.runtime/bootstrap/interpreter.go (1)
550-554: Fix stale doc comment abovesplitTopLevel.The comment currently describes
SplitKeywordArguments, which does not match the function below and can mislead future maintenance.Suggested doc fix
-// SplitKeywordArguments separates positional arguments from a trailing -// KeywordArguments payload. -// -// positional, keywordArguments := runtime.SplitKeywordArguments(arguments) +// splitTopLevel splits by `separator` while respecting nested groupings +// and quoted string literals. func splitTopLevel(value string, separator rune) ([]string, error) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@runtime/bootstrap/interpreter.go` around lines 550 - 554, The doc comment above splitTopLevel is stale (mentions SplitKeywordArguments); update it to accurately describe splitTopLevel: explain that splitTopLevel splits the input string by the given separator rune at top-level (i.e., it does not split when the separator is inside nested structures or quoted segments) and state the returned values (a slice of top-level segments and an error). Place this corrected comment immediately above the splitTopLevel function declaration and ensure it follows Go doc comment style (starts with the function name).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: cee01306-efa2-4b7e-afb1-013fb325d076
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (106)
.gitignoreAGENTS.mdREADME.mdRFC.mdbindings/action/action.gobindings/agent/agent.gobindings/api/api.gobindings/array/array.gobindings/cache/cache.gobindings/config/config.gobindings/container/container.gobindings/crypto/crypto.gobindings/data/data.gobindings/dns/dns.gobindings/echo/echo.gobindings/entitlement/entitlement.gobindings/err/err.gobindings/fs/fs.gobindings/i18n/i18n.gobindings/info/info.gobindings/json/json.gobindings/log/log.gobindings/math/math.gobindings/mcp/mcp.gobindings/medium/medium.gobindings/options/options.gobindings/path/path.gobindings/process/process.gobindings/register/register.gobindings/registry/registry.gobindings/scm/scm.gobindings/service/service.gobindings/stdlib/stdlib.gobindings/store/store.gobindings/strings/strings.gobindings/task/task.gobindings/typemap/typemap.gobindings/ws/ws.gocmd/corepy/main.gocmd/corepy/main_test.godocs/gpython-readiness.mdexamples/echo.pyexamples/filesystem.pyexamples/math.pyexamples/primitive_pipeline.pyexamples/signal.pyexamples/stdlib_shadow.pygo.modpy/build/README.mdpy/build/_build_stub.pypy/build/build.shpy/build/core/__init__.pypy/build/pyproject.tomlpy/build/tests/test_tier2_stub.pypy/core/__init__.pypy/core/action.pypy/core/agent.pypy/core/api.pypy/core/array.pypy/core/cache.pypy/core/config.pypy/core/container.pypy/core/crypto.pypy/core/data.pypy/core/dns.pypy/core/entitlement.pypy/core/err.pypy/core/fs.pypy/core/i18n.pypy/core/info.pypy/core/json.pypy/core/log.pypy/core/math/__init__.pypy/core/math/_shared.pypy/core/math/kdtree.pypy/core/math/knn.pypy/core/math/signal.pypy/core/mcp.pypy/core/medium.pypy/core/options.pypy/core/path.pypy/core/process.pypy/core/py.typedpy/core/registry.pypy/core/scm.pypy/core/service.pypy/core/store.pypy/core/strings.pypy/core/task.pypy/core/ws.pypy/pyproject.tomlpy/tests/test_core.pypy/tests/test_medium_qdrant.pypy/tests/test_rfc_stub_modules.pyruntime/bootstrap/interpreter.goruntime/examples_test.goruntime/gpython/interpreter.goruntime/gpython_disabled.goruntime/gpython_enabled.goruntime/gpython_smoke_test.goruntime/internal/contract/types.goruntime/interpreter.goruntime/interpreter_test.goruntime/rfc_stub_modules_test.goruntime/tier2/runner.goruntime/tier2/runner_test.go
| if handlerType.NumIn() == 1 { | ||
| argumentType := handlerType.In(0) | ||
| if argumentType.Kind() == reflect.Interface { | ||
| callArguments = append(callArguments, reflect.ValueOf(options)) | ||
| } else if reflect.TypeOf(options).AssignableTo(argumentType) { | ||
| callArguments = append(callArguments, reflect.ValueOf(options)) | ||
| } else { | ||
| return nil, fmt.Errorf("action handler parameter is not supported: %s", argumentType) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify reflection call/return handling patterns that can panic.
rg -n 'argumentType.Kind\(\) == reflect.Interface|AssignableTo\(argumentType\)|returnValues\[1\]\.IsNil|case 2:' bindings/action/action.goRepository: dAppCore/py
Length of output: 225
🏁 Script executed:
sed -n '200,245p' bindings/action/action.go | cat -nRepository: dAppCore/py
Length of output: 1918
🏁 Script executed:
rg -n 'IsNil\(\)|Implements\(' bindings/action/action.goRepository: dAppCore/py
Length of output: 91
🏁 Script executed:
# Check if there are any other Call() invocations that might have similar issues
rg -n '\.Call\(' bindings/action/action.go -A 3 -B 3Repository: dAppCore/py
Length of output: 196
Add reflection safety checks to prevent runtime panics in handler invocation.
Two critical panic paths exist:
-
Interface parameter without type validation (line 213–220): When
argumentTypeis an interface, the code appendsoptionsto call arguments without checking ifoptionsimplements that interface. If the handler signature isfunc(io.Reader)andoptionsdoes not implementio.Reader, theCall()invocation at line 222 will panic. -
Unsafe
IsNil()call on second return value (line 233–235): The code callsIsNil()onreturnValues[1]without ensuring it is nilable. If a handler returns(string, int)or any non-nilable second value,IsNil()panics. Additionally, the type assertion lacks an ok-check before use.
Apply the suggested fix to add Implements() validation for interface parameters and replace unsafe IsNil() with a nil-safe check via Interface() followed by proper type assertion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bindings/action/action.go` around lines 211 - 219, The handler invocation
currently assumes options satisfies interface params and unsafely calls IsNil()
on returnValues; update the argument handling in the handlerType/argumentType
branch to first check if argumentType.Kind() == reflect.Interface then use
argumentType.Implements(reflect.TypeOf(options)) (or
reflect.TypeOf(options).AssignableTo when appropriate) before appending
reflect.ValueOf(options) to callArguments to avoid Call() panics, and when
inspecting the second return value in returnValues replace direct IsNil() use
with a nil-safe approach: first check len(returnValues) > 1, then get rv :=
returnValues[1] and if rv.Kind() == reflect.Interface || rv.Kind() ==
reflect.Ptr || rv.Kind() == reflect.Slice || rv.Kind() == reflect.Map ||
rv.Kind() == reflect.Func || rv.Kind() == reflect.Chan { if rv.IsNil() { /*
handle nil error */ } else if err, ok := rv.Interface().(error); ok { /* use err
*/ } } else if err, ok := rv.Interface().(error); ok { /* use non-nilable error
*/ } to ensure you perform a safe Interface() extraction and error type
assertion with ok-check rather than calling IsNil() unguarded; reference
handlerType, argumentType, options, Call(), returnValues, IsNil() in your
changes.
| steps, err := parseSteps(arguments[2], "core.task.register") | ||
| if err != nil { | ||
| return nil, err |
There was a problem hiding this comment.
Guard arguments[2] before use in registerTask.
Line 147 can panic on short calls because arguments[2] is indexed without a length check.
Proposed fix
func registerTask(arguments ...any) (any, error) {
registryValue, err := expectRegistry(arguments, 0, "core.task.register")
if err != nil {
return nil, err
}
name, err := typemap.ExpectString(arguments, 1, "core.task.register")
if err != nil {
return nil, err
}
+ if len(arguments) < 3 {
+ return nil, fmt.Errorf("core.task.register expected argument 2")
+ }
steps, err := parseSteps(arguments[2], "core.task.register")
if err != nil {
return nil, err
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| steps, err := parseSteps(arguments[2], "core.task.register") | |
| if err != nil { | |
| return nil, err | |
| if len(arguments) < 3 { | |
| return nil, fmt.Errorf("core.task.register expected argument 2") | |
| } | |
| steps, err := parseSteps(arguments[2], "core.task.register") | |
| if err != nil { | |
| return nil, err | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bindings/task/task.go` around lines 147 - 149, registerTask currently indexes
arguments[2] without checking arguments length which can panic; update
registerTask to first validate len(arguments) >= 3 before calling parseSteps (or
handle missing argument case), and return a descriptive error (or use a sensible
default) if the steps argument is absent; specifically add the guard around the
parseSteps call in registerTask to avoid indexing arguments[2] when the slice is
too short.
| digest = hashlib.sha256(fs.read_file(target)) | ||
| print(os.path.basename(target)) | ||
| print(base64.b64encode(path.base(target))) | ||
| print(digest.hexdigest()) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the concrete definitions for the helpers used in this example.
rg -nP 'def\s+(read_file|base)\b|func\s+.*(ReadFile|Base)\b' py bindings runtime -C 3
# Check whether nearby code already encodes/decodes these values before hashing or printing.
rg -nP 'sha256\(|b64encode\(|decode\(\)' examples py/tests py/core bindings runtime -C 2Repository: dAppCore/py
Length of output: 5248
Convert string values to bytes before passing to stdlib functions.
The code at lines 13 and 15 will fail at runtime:
- Line 13:
hashlib.sha256()requires bytes-like input, butfs.read_file()returns astr - Line 15:
base64.b64encode()requires bytes-like input, butpath.base()returns astr
Encode both values to bytes first. The codebase pattern (e.g. py/core/crypto.py) uses a _bytes() helper, and then decodes the base64 result:
digest = hashlib.sha256(fs.read_file(target).encode())
print(base64.b64encode(path.base(target).encode()).decode('ascii'))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/stdlib_shadow.py` around lines 13 - 16, The calls to hashlib.sha256
and base64.b64encode are passing str values causing runtime errors; change the
inputs to bytes by encoding the strings (or use the existing _bytes(...) helper)
before calling hashlib.sha256(fs.read_file(target)) and
base64.b64encode(path.base(target)), and when printing the base64 result decode
it back to text (e.g. decode('ascii')) so print receives a str; update the
expressions that call hashlib.sha256, fs.read_file, base64.b64encode, and
path.base accordingly.
Summary by CodeRabbit
Release Notes
New Features
run,repl, andmodulescommands; Tier 2 CPython fallback for ML workloadsDocumentation
Infrastructure